#ifndef __SERVER_MANAGER_H__
#define __SERVER_MANAGER_H__

#include <cstdint>
#include <atomic>
#include <mutex>
#include <memory>
#include <set>
#include <unordered_map>

#include "Engine/GameGuid.h"

#include "SimpleSingleton.hpp"
#include "blockingconcurrentqueue.h"

#include "LiteNetLib/INetEventListener.h"
#include "LiteNetLib/NetManager.h"

#include "GameType.h"
#include "GameLock.hpp"


using namespace LiteNetLib;
using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    class IPackage;
    
    class ServerRoot : public SimpleSingletonItem, private INetEventListener
    {
        friend class SimpleSingleton;
        friend class ConnectionManager;

        const float serverRootSaveDataTime = 300.0f;

    public:
        // 运行服务器，阻塞运行
        void serverRun();

        // 只有在运行过程中才能调用，调用服务器会停止，阻塞会结束
        inline void serverStop();

        /**
         * @brief 通过 guid 查找对应的客户端
         * @return nullptr:没有查找到客户端
        */
        ClientPtr findClientByGuid(const GameGuid& guid);
        
        /**
         * @brief 通过 地址hash值 查找对应的客户端
         * @return nullptr:没有查找到客户端
        */
        ClientPtr findClientByAddrHash(size_t addressHashCode);

        /**
         * @brief 获取所有的客户端，包括没有加载 PlayerData 的客户端
         * @param outClients 客户端列表 
        */
        void getAllClients(std::vector<ClientPtr> &outClients);

        // 获取但前连接的客户端数量
        inline uint32_t getClientCount();

        /**
         * @brief 控制台输入消息
         * @param message 控制台输入的内容,这应该是一整行消息，不能带回车键
        */
        inline void addTerminalMessage(const std::string &message);

        /**
         * @brief 向对应的地址透传 udp 数据
         * @param buff 数据
         * @param buffSize 数据大小
         * @param addr ip地址
        */
        inline void sendTo(const char *buff, uint32_t buffSize, net_endpoint *addr);

        /**
         * @brief 下一帧后对服务器数据进行保存
        */
        inline void nextFrameSave();

    private:

        ServerRoot();
        ~ServerRoot();

        virtual void OnPeerConnected(NetPeerPtr &peer) override;

        // 收到客户端断开
        virtual void OnPeerDisconnected(NetPeerPtr &peer, DisconnectReason reason, const char* packet, uint32_t packetLength) override;

        // 收到客户端消息
        virtual void OnNetworkReceive(NetPeerPtr &peer, const char* packet, uint32_t packetLength, DeliveryMethod deliveryMethod) override;

        // 收到非客户端的消息
        virtual void OnNetworkReceiveUnconnected(net_endpoint *endpoint, const char* packet, uint32_t packetLength, UnconnectedMessageType messageType) override;

        // 收到连接请求
        virtual void OnConnectionRequest(ConnectionRequest *request) override;

        // 服务器的帧处理
        void tick(float dt);

        /**
         * @brief 通过地址 Hash 值来移除对应的游戏客户端
         * @param addressHashCode 地址 Hash 值
        */
        void removeClientByAddressHashCode(size_t addressHashCode);

        /**
         * @brief 创建游戏客户端对象，若对象创建成将会存到内部对象表中，没有创建则会返回 nullptr
         * @param peer LiteNetLib客户端对象
         * @param userGuid 玩家的GUID
         * @param nickname 玩家的游戏名称，没有写 "" 即可
        */
        ClientPtr createClient(NetPeerPtr peer, const GameGuid &userGuid, const std::string &nickname);

        /**
         * @brief 数据保存
         * @note 内部有调用 wait_for_tasks()
        */
        void save();

    private:
        // 控制台打印的消息会先存储在队列中，然后再取出
        moodycamel::BlockingConcurrentQueue<std::string> m_terminalQueue;

        // 用于表示服务器是否运行中，serverRun 调用后该值为 true， 之后调用 serverStop 会将该值改为 false 后内部循环将停止
        std::atomic<bool> m_IsRun;

        // 用于存放分配给客户端的id，这里的 set 是会自动由低到高排序，这里有255个客户端的限制，超了会崩服
        std::set<uint8_t> m_clientIdSet;

        // 存放 client 的 map， key 为地址的 hashCode
        std::unordered_map<size_t, ClientPtr> m_clientsMap;
        // 这个是方便通过id来找到对应的客户端，查找的时候跟 m_clients 用的同一个锁：m_clientMutex
        ClientPtr m_clientIdMap[UINT8_MAX] = { nullptr };
        SpinLock m_clientsMapMutex;

        // LiteNetLib 服务对象
        NetManager *m_netManager = nullptr;

        float m_saveTime;
    };

    inline void ServerRoot::nextFrameSave()
    {
        m_saveTime = serverRootSaveDataTime;
    }

    inline void ServerRoot::serverStop()
    {
        m_IsRun = false;
    }
    inline uint32_t ServerRoot::getClientCount()
    {
        std::lock_guard<SpinLock> lk(m_clientsMapMutex);
        return m_clientsMap.size();
    }
    inline void ServerRoot::addTerminalMessage(const std::string &message)
    {
        m_terminalQueue.enqueue(message);
    }
    inline void ServerRoot::sendTo(const char *buff, uint32_t buffSize, net_endpoint *endpoint)
    {
        m_netManager->sendRaw(buff, buffSize, endpoint);
    }

}

#endif